/**
 * @file EnumDevices.cpp
 * @brief 设备枚举函数实现
 * @details 提供设备枚举功能的实现
 */

#include "EnumDevices.h"
#include <Windows.h>
#include <SetupAPI.h>
#include <QDebug>
#include <vector>
#include <functional>


static int EnumDevice(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDevInfoData, DeviceParams* pDevParams)
{
    int res = IDCONTINUE;

    // 如提供用户回调，则调用之；回调返回 false 表示取消
    if (pDevParams->enumParams->callback) {
        if (!pDevParams->enumParams->callback(hDevInfo,
                                              pDevInfoData,
                                              &pDevParams->deviceProperties,
                                              pDevParams->enumParams->rebootRequired,
                                              pDevParams->enumParams->callbackParam))
        {
            res = IDCANCEL;
        }
    }
    // 计数命中设备数量（无论是否取消）
    pDevParams->enumParams->deviceCount++;

    return res;
}

int EnumDevices(
    DeviceEnumFilter pFilter,
    DWORD flags,
    DeviceRoutine pDevRoutine,
    DeviceEnumParams* pEnumParams)
{
    HDEVINFO hDevInfo = SetupDiGetClassDevs(
        nullptr,
        nullptr,
        nullptr,
        DIGCF_ALLCLASSES | flags
        );

    if (hDevInfo == INVALID_HANDLE_VALUE) {
        qDebug() << "获取设备信息集失败";
        return IDCANCEL;
    }

    int res = IDCONTINUE;
    SP_DEVINFO_DATA devInfoData;
    devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);

    // 遍历所有设备
    for (DWORD i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &devInfoData); i++) {
        // 获取硬件ID（REG_MULTI_SZ）
        DWORD requiredSize = 0;
        SetupDiGetDeviceRegistryProperty(
            hDevInfo, &devInfoData, SPDRP_HARDWAREID,
            nullptr, nullptr, 0, &requiredSize
            );
        if (requiredSize == 0) {
            continue;
        }
        std::vector<char> hardwareIdBuffer(requiredSize);
        if (!SetupDiGetDeviceRegistryProperty(
                hDevInfo, &devInfoData, SPDRP_HARDWAREID,
                nullptr, reinterpret_cast<PBYTE>(hardwareIdBuffer.data()),
                requiredSize, nullptr)) {
            continue;
        }
        const char* hardwareId = hardwareIdBuffer.data();

        // 应用硬件ID过滤器（如果提供）
        if (pFilter && !pFilter(hardwareId)) {
            continue;
        }

        // 检查设备属性匹配性
        QString pHardwareId;
        if (pEnumParams->targetProperties && !pEnumParams->targetProperties->getDeviceId().isEmpty()) {
            // 获取HwId列表
            // 解析 REG_MULTI_SZ: 多个以 '\0' 分隔，以双 '\0' 结束
            QStringList ids;
            const char* p = hardwareId;
            const char* end = hardwareId + requiredSize;
            while (p < end && *p != '\0') {
                QString id = QString::fromLocal8Bit(p);
                if (!id.isEmpty()) ids.push_back(id);
                p += std::strlen(p) + 1;
            }
            // 检查是否与查询匹配
            while (!ids.isEmpty()) {
                QString hwId = ids.first();
                if (hwId.contains(pEnumParams->targetProperties->getDeviceId())) {
                    pHardwareId = hwId;
                }
                ids.pop_front();
            }
            if (pHardwareId.isEmpty()) {
                continue;
            }
        } else {
            pHardwareId = hardwareId;
        }

        DeviceParams devParams(pEnumParams);
        devParams.deviceProperties.setDeviceId(pHardwareId);

        // 读取 Location 信息（如果可用），并按目标 Location 过滤
        std::vector<char> locationBuffer(40);
        if (SetupDiGetDeviceRegistryProperty(hDevInfo,
                                             &devInfoData,
                                             SPDRP_LOCATION_INFORMATION,
                                             nullptr,
                                             reinterpret_cast<PBYTE>(locationBuffer.data()),
                                             (DWORD)locationBuffer.size(),
                                             nullptr)) {
            devParams.deviceProperties.setLocation(locationBuffer.data());
        }
        if (pEnumParams->targetProperties
            && !pEnumParams->targetProperties->getLocation().isEmpty()
            && (devParams.deviceProperties.getLocation().isEmpty()
                || !(devParams.deviceProperties.getLocation() == pEnumParams->targetProperties->getLocation()))) {
            continue;
        }

        // 读取物理对象名（如果可用），并按目标物理对象名过滤
        std::vector<char> nameBuffer(40);
        if (SetupDiGetDeviceRegistryProperty(hDevInfo,
                                             &devInfoData,
                                             SPDRP_PHYSICAL_DEVICE_OBJECT_NAME,
                                             nullptr,
                                             reinterpret_cast<PBYTE>(nameBuffer.data()),
                                             (DWORD)nameBuffer.size(),
                                             nullptr)) {
            devParams.deviceProperties.setPhysicalObjectName(nameBuffer.data());
        }
        if (pEnumParams->targetProperties
            && !pEnumParams->targetProperties->getPhysicalObjectName().isEmpty()
            && (devParams.deviceProperties.getPhysicalObjectName().isEmpty()
                || !(devParams.deviceProperties.getPhysicalObjectName() == pEnumParams->targetProperties->getPhysicalObjectName()))) {
            continue;
        }

        // 调用设备处理例程（内部将调用用户设置的回调）
        res = pDevRoutine(hDevInfo, &devInfoData, &devParams);
        if (res != IDCONTINUE) {
            break;
        }
    }

    SetupDiDestroyDeviceInfoList(hDevInfo);
    return res;
}

/**
 * @brief 枚举设备
 * @param pFilter 设备枚举过滤器
 * @param pDevProperties 设备属性指针
 * @param pDevCallBack 设备回调函数
 * @param pDevCallBackParam 回调函数参数
 * @return 枚举到的设备数量，失败返回负数
 */
int EnumDevices(
    DeviceEnumFilter pFilter,
    ConstDevicePropertiesPtr pDevProperties,
    bool *pRebootRequired,
    DeviceCallback pDevCallBack,
    void *pDevCallBackParam)
{
    DeviceEnumParams enumParams;

    enumParams.targetProperties = pDevProperties;
    enumParams.rebootRequired = pRebootRequired;
    enumParams.callback = pDevCallBack;
    enumParams.callbackParam = pDevCallBackParam;

    // 使用 DIGCF_PRESENT 仅枚举当前存在的设备
    if (EnumDevices(pFilter, DIGCF_PRESENT, EnumDevice, &enumParams) != IDCONTINUE)
        return -1;

    return enumParams.deviceCount;
}
